﻿using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Newtonsoft.Json;
using System;
using System.Text;
using webapi.DTO;
using webapi.Helper;
using webapi.Model;

namespace webapi.Controllers
{
    [ApiController]
    [Route("[controller]")]
    [Authorize]
    public class UserAccountController : ControllerBase
    {

        private readonly ILogger<UserAccountController> _logger;

        private DbContextHelper _dbContextHelper;

        private HashHelper _hashHelper;

        private EmailHelper _emailHelper;
        IConfiguration _configuration;
        public UserAccountController(ILogger<UserAccountController> logger, DbContextHelper dbContextHelper, IConfiguration configuration)
        {
            _logger = logger;
            _dbContextHelper = dbContextHelper;
            _hashHelper = new HashHelper();
            _emailHelper = new EmailHelper();
            _configuration = configuration;
        }


        [HttpPost("CreateUserAccount")]
        [AllowAnonymous]
        public OutputDTO CreateUserAccount([FromBody] UserSignInDTO json)
        {
            var hashedPwd = _hashHelper.HashPassword(json.password);
            var userInfoGuid = Guid.NewGuid().ToString();
            Random random = new Random();
            var userId = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds().ToString() + random.Next(100, 1000);

            var res = _dbContextHelper.EmailVerifyTmpModels.Where(p => p.EMAIL_ADRESS == json.email).FirstOrDefault();

            #region 输入验证
            if (string.IsNullOrWhiteSpace(json.email))
            {
                return new OutputDTO()
                {
                    error_code = "2001",
                    status = "200",
                    msg = "Email can not be null."
                };
            }
            if (!json.email.Contains("@"))
            {
                return new OutputDTO()
                {
                    error_code = "2001",
                    status = "200",
                    msg = "Email illegal."
                };
            }
            if (string.IsNullOrWhiteSpace(json.password))
            {
                return new OutputDTO()
                {
                    error_code = "2001",
                    status = "200",
                    msg = "Password can not be null."
                };
            }
            if (json.password.Length < 8 || json.password.Length > 18)
            {
                return new OutputDTO()
                {
                    error_code = "2001",
                    status = "200",
                    msg = "The password should be between 8 and 18 digits."
                };
            }
            if (json.password != json.confirmPassword)
            {
                return new OutputDTO()
                {
                    error_code = "2001",
                    status = "200",
                    msg = "The confirm password and password do not match."
                };
            }
            if (string.IsNullOrWhiteSpace(json.dAdress))
            {
                return new OutputDTO()
                {
                    error_code = "2001",
                    status = "200",
                    msg = "Delivery adress can not be null."
                };
            }
            if (string.IsNullOrWhiteSpace(json.phone))
            {
                return new OutputDTO()
                {
                    error_code = "2001",
                    status = "200",
                    msg = "Phone number can not be null."
                };
            }
            var checkEmail = _dbContextHelper.UserInfo.Where(u => u.EMAIL_ADDRESS == json.email).FirstOrDefault();

            if (checkEmail != null)
            {
                return new OutputDTO()
                {
                    error_code = "2001",
                    status = "200",
                    msg = "Email duplicate."
                };
            }
            if (res != null)
            {
                long timeStampNow = DateTime.UtcNow.Ticks;
                long timeStampTmp = long.Parse(res.TIMESTAMP);
                TimeSpan difference = TimeSpan.FromTicks(Math.Abs(timeStampNow - timeStampTmp));
                bool isGreaterThanTenMinutes = difference <= TimeSpan.FromMinutes(10);
                if (!isGreaterThanTenMinutes)
                {
                    return new OutputDTO()
                    {
                        error_code = "2001",
                        status = "200",
                        msg = "Verification code expired."
                    };
                }
                if (json.verifyCode != res.VERIFY_CODE)
                {
                    return new OutputDTO()
                    {
                        error_code = "2001",
                        status = "200",
                        msg = "Verify Code Incorrect!"
                    };
                }
            }
            else
            {
                return new OutputDTO()
                {
                    error_code = "2001",
                    status = "200",
                    msg = "Please Send The Verify Code!"
                };
            }
            #endregion

            UserAllModel userModel = new UserAllModel();
            userModel.GUID = Guid.NewGuid().ToString();
            userModel.USER_INFO_GUID = userInfoGuid;
            userModel.USER_ID = userId.ToString();
            userModel.SIGN_UP_DATETIME = DateTime.Now;

            UserInfoModel userInfoModel = new UserInfoModel();
            userInfoModel.GUID = userInfoGuid;
            userInfoModel.USER_GROUP_ID = "0";
            userInfoModel.USER_PASSWORD = hashedPwd;
            userInfoModel.AVATAR = "";
            userInfoModel.USER_ID = userId.ToString();
            userInfoModel.EMAIL_ADDRESS = json.email;
            userInfoModel.DELIVERY_ADRESS = json.dAdress;
            userInfoModel.ZIPCODE = json.zipCode;
            userInfoModel.PHONE_NUMBER = json.phone;
            if (string.IsNullOrWhiteSpace(json.fname) && string.IsNullOrWhiteSpace(json.lname))
            {
                userInfoModel.NICKNAME = "User" + DateTimeOffset.UtcNow.ToUnixTimeMilliseconds().ToString();
            }
            else
            {
                userInfoModel.NICKNAME = json.lname + " " + json.fname;
            }

            _dbContextHelper.Users.Add(userModel);
            _dbContextHelper.UserInfo.Add(userInfoModel);
            _dbContextHelper.SaveChanges();
            //_dbContextHelper.Users.ToList()
            //string userJson = JsonConvert.SerializeObject(_dbContextHelper.Users.ToList());
            return new OutputDTO()
            {
                error_code = "2000",
                status = "200",
                msg = ""
            };
        }

        [HttpPost("Login")]
        [AllowAnonymous]
        public LoginOutputDTO UserLogin([FromBody] UserLoginDTO json)
        {
            var user = _dbContextHelper.UserInfo.Where(p => p.DELETE_DATE == null && p.EMAIL_ADDRESS == json.email).FirstOrDefault();
            if (user == null)
            {
                return new LoginOutputDTO()
                {
                    error_code = "2001",
                    status = "200",
                    msg = "Email does not exist."
                };
            }
            var checkPwd = _hashHelper.VerifyPassword(json.password, user.USER_PASSWORD);
            var token = new JwtHelper(_configuration).GenerateJwtToken(new Model.UserJwt() { UserEmail = json.email, UserGroupId = user.USER_GROUP_ID, UserGuid = user.GUID });
            if (checkPwd)
            {
                //string accessToken = JwtHelper.GetToken(user.EMAIL_ADDRESS);
                return new LoginOutputDTO()
                {
                    error_code = "2000",
                    status = "200",
                    msg = "Login succeed.",
                    data = new UserInfo()
                    {
                        user_guid = user.GUID,
                        access_token = "",
                        deliver_adress = user.DELIVERY_ADRESS,
                        email_adress = user.EMAIL_ADDRESS,
                        nick_name = user.NICKNAME,
                        user_group_id = user.USER_GROUP_ID,
                        zip_code = user.ZIPCODE,
                        phone_number = user.PHONE_NUMBER,
                        token = token
                    }
                };
            }
            else
            {
                return new LoginOutputDTO()
                {
                    error_code = "2001",
                    status = "200",
                    msg = "Password incorrect."
                };
            }
        }

        [HttpPost("GetEmailVerify")]
        [AllowAnonymous]
        public OutputDTO GetEmailVerify([FromBody] GetEmailVerifyInput input)
        {
            EmailVerifyTmpModel tmp = new EmailVerifyTmpModel();
            if (input == null)
            {
                return new OutputDTO()
                {
                    error_code = "2001",
                    status = "200",
                    msg = "Please Enter Your Email."
                };
            }
            if (string.IsNullOrWhiteSpace(input.emailAdress))
            {
                return new OutputDTO()
                {
                    error_code = "2001",
                    status = "200",
                    msg = "Please Enter Your Email."
                };
            }
            var existEmail = _dbContextHelper.UserInfo.Where(p => p.DELETE_DATE == null).Where(p => p.EMAIL_ADDRESS == input.emailAdress).FirstOrDefault();

            if (existEmail == null)
            {
                var res = _dbContextHelper.EmailVerifyTmpModels.Where(p => p.EMAIL_ADRESS == input.emailAdress).FirstOrDefault();

                Random random = new Random();
                StringBuilder sb = new StringBuilder();
                for (int i = 0; i < 6; i++)
                {
                    sb.Append(random.Next(10));
                }
                string randomDigits = sb.ToString();
                if (res == null)
                {
                    tmp.GUID = Guid.NewGuid().ToString();
                    tmp.VERIFY_CODE = randomDigits;
                    tmp.EMAIL_ADRESS = input.emailAdress;
                    tmp.TIMESTAMP = DateTime.UtcNow.Ticks.ToString();
                    _dbContextHelper.EmailVerifyTmpModels.Add(tmp);
                }
                else
                {
                    res.TIMESTAMP = DateTime.UtcNow.Ticks.ToString();
                    res.VERIFY_CODE = randomDigits;
                }
                _dbContextHelper.SaveChanges();
                _emailHelper.SendMessage(input.emailAdress, randomDigits);

                return new OutputDTO()
                {
                    error_code = "2000",
                    status = "200",
                    msg = "Verify code has been sent."
                };
            }
            return new OutputDTO()
            {
                error_code = "2001",
                status = "200",
                msg = "Email Already Exists."
            };
        }

        [HttpPost("GetEmailReVerify")]
        [AllowAnonymous]
        public OutputDTO GetEmailReVerify([FromBody] GetEmailVerifyInput input)
        {
            EmailVerifyTmpModel tmp = new EmailVerifyTmpModel();
            if (input == null)
            {
                return new OutputDTO()
                {
                    error_code = "2001",
                    status = "200",
                    msg = "Please Enter Your Email."
                };
            }
            if (string.IsNullOrWhiteSpace(input.emailAdress))
            {
                return new OutputDTO()
                {
                    error_code = "2001",
                    status = "200",
                    msg = "Please Enter Your Email."
                };
            }
            var existEmail = _dbContextHelper.UserInfo.Where(p => p.DELETE_DATE == null).Where(p => p.EMAIL_ADDRESS == input.emailAdress).FirstOrDefault();

            if (existEmail != null)
            {
                var res = _dbContextHelper.EmailVerifyTmpModels.Where(p => p.EMAIL_ADRESS == input.emailAdress).FirstOrDefault();

                Random random = new Random();
                StringBuilder sb = new StringBuilder();
                for (int i = 0; i < 6; i++)
                {
                    sb.Append(random.Next(10));
                }
                string randomDigits = sb.ToString();
                if (res == null)
                {
                    tmp.GUID = Guid.NewGuid().ToString();
                    tmp.VERIFY_CODE = randomDigits;
                    tmp.EMAIL_ADRESS = input.emailAdress;
                    tmp.TIMESTAMP = DateTime.UtcNow.Ticks.ToString();
                    _dbContextHelper.EmailVerifyTmpModels.Add(tmp);
                }
                else
                {
                    res.TIMESTAMP = DateTime.UtcNow.Ticks.ToString();
                    res.VERIFY_CODE = randomDigits;
                }
                _dbContextHelper.SaveChanges();
                _emailHelper.SendMessage(input.emailAdress, randomDigits);

                return new OutputDTO()
                {
                    error_code = "2000",
                    status = "200",
                    msg = "Verify code has been sent."
                };
            }
            return new OutputDTO()
            {
                error_code = "2001",
                status = "200",
                msg = "Email Not Exists."
            };
        }

        [HttpPost("ChangePassword")]
        [AllowAnonymous]
        public OutputDTO ChangePassword([FromBody] ChangePasswordInput input)
        {
            if (!string.IsNullOrWhiteSpace(input.verifyCode) && !string.IsNullOrWhiteSpace(input.password) && !string.IsNullOrWhiteSpace(input.email))
            {
                var res = _dbContextHelper.EmailVerifyTmpModels.Where(p => p.EMAIL_ADRESS == input.email).FirstOrDefault();
                if (input.password.Length < 8 || input.password.Length > 18)
                {
                    return new OutputDTO()
                    {
                        error_code = "2001",
                        status = "200",
                        msg = "The password should be between 8 and 18 digits."
                    };
                }
                if (res != null)
                {
                    long timeStampNow = DateTime.UtcNow.Ticks;
                    long timeStampTmp = long.Parse(res.TIMESTAMP);
                    TimeSpan difference = TimeSpan.FromTicks(Math.Abs(timeStampNow - timeStampTmp));
                    bool isGreaterThanTenMinutes = difference <= TimeSpan.FromMinutes(10);
                    if (!isGreaterThanTenMinutes)
                    {
                        return new OutputDTO()
                        {
                            error_code = "2001",
                            status = "200",
                            msg = "Verification code expired."
                        };
                    }
                    if (input.verifyCode != res.VERIFY_CODE)
                    {
                        return new OutputDTO()
                        {
                            error_code = "2001",
                            status = "200",
                            msg = "Verify Code Incorrect!"
                        };
                    }
                }
                else
                {
                    return new OutputDTO()
                    {
                        error_code = "2001",
                        status = "200",
                        msg = "Please Send The Verify Code!"
                    };
                }
                var user = _dbContextHelper.UserInfo.Where(p => p.EMAIL_ADDRESS == input.email).FirstOrDefault();
                if (user != null)
                {
                    user.USER_PASSWORD = _hashHelper.HashPassword(input.password);
                }
                _dbContextHelper.SaveChanges();
                return new OutputDTO()
                {
                    error_code = "2000",
                    status = "200",
                    msg = "Change Password Complete."
                };
            }
            return new OutputDTO()
            {
                error_code = "2001",
                status = "200",
                msg = "Input Can not be null."
            };
        }

        [HttpPost("ChangeProfile")]
        [Authorize]
        public LoginOutputDTO ChangeProfile(ChangeProfileInput input)
        {
            if (!string.IsNullOrWhiteSpace(input.email) && !string.IsNullOrWhiteSpace(input.deliverAddress) && !string.IsNullOrWhiteSpace(input.zipCode))
            {
                var user = _dbContextHelper.UserInfo.Where(p => p.EMAIL_ADDRESS == input.email).FirstOrDefault();
                if (user != null)
                {
                    if (string.IsNullOrWhiteSpace(input.fName) && string.IsNullOrWhiteSpace(input.lName))
                    {
                        user.ZIPCODE = input.zipCode;
                        user.DELIVERY_ADRESS = input.deliverAddress;
                        user.PHONE_NUMBER = input.phone_number;
                    }
                    else
                    {
                        user.NICKNAME = input.lName + " " + input.fName;
                        user.ZIPCODE = input.zipCode;
                        user.DELIVERY_ADRESS = input.deliverAddress;
                        user.PHONE_NUMBER = input.phone_number;
                    }
                    _dbContextHelper.SaveChanges();
                    return new LoginOutputDTO()
                    {
                        data = new UserInfo()
                        {
                            deliver_adress = input.deliverAddress,
                            zip_code = input.zipCode,
                            phone_number = input.phone_number
                        },
                        error_code = "2000",
                        status = "200",
                        msg = "Change Profile Complete."
                    };
                }
                return new LoginOutputDTO()
                {
                    error_code = "2001",
                    status = "200",
                    msg = "user not exist."
                };
            }
            return new LoginOutputDTO()
            {
                error_code = "2001",
                status = "200",
                msg = "Input Can not be null."
            };
        }

        [HttpPost("GetUserIsAdmin")]
        [Authorize]
        public GetUserIsAdminOutput GetUserIsAdmin()
        {
            var userClaim = HttpContext.User.Claims;
            var userGuid = userClaim.FirstOrDefault(c => c.Type == "UserGuid")?.Value;
            var userGroupId = userClaim.FirstOrDefault(c => c.Type == "UserGroupId")?.Value;
            if (userGroupId == "0")
            {
                return new GetUserIsAdminOutput()
                {
                    is_admin = false,
                    error_code = "2000",
                    status = "200",
                    msg = "",
                };
            }
            return new GetUserIsAdminOutput()
            {
                is_admin = true,
                error_code = "2000",
                status = "200",
                msg = "",
            };
        }
    }
}
